package io.github.igordonxiao.gateguard.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * 认证服务器配置
 */
@Configuration
@EnableAuthorizationServer
public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    private final AuthenticationManager authenticationManager;
    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userService;
    private final Oauth2ExceptionTranslator oauth2ExceptionTranslator;
    private final GuardRedisTokenStore guardRedisTokenStore;

    public Oauth2AuthorizationServerConfig(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder, UserDetailsService userService, Oauth2ExceptionTranslator oauth2ExceptionTranslator, GuardRedisTokenStore guardRedisTokenStore) {
        this.authenticationManager = authenticationManager;
        this.passwordEncoder = passwordEncoder;
        this.userService = userService;
        this.oauth2ExceptionTranslator = oauth2ExceptionTranslator;
        this.guardRedisTokenStore = guardRedisTokenStore;
    }

    /**
     * Basic认证客户端ID
     */
    @Value("${jwt.basic.client-id:gate}")
    private String clientId;

    /**
     * Basic认证客户端密码
     */
    @Value("${jwt.basic.client-secret:guard}")
    private String clientSecret;

    /**
     * JWT签名Key
     */
    @Value("${jwt.signing-key:gordon}")
    private String jwtSigningKey;

    /**
     * JWT访问Token超时时间
     * 默认：43200=12 hours
     */
    @Value("${jwt.access-token-validity-seconds:43200}")
    private int accessTokenValiditySeconds;

    /**
     * JWT刷新Token超时时间
     * 默认：2592000=30 days
     */
    @Value("${jwt.refresh-token-validity-seconds:2592000}")
    private int refreshTokenValiditySeconds;

    /**
     * Security OAuth2认证模式
     */
    @Value("${jwt.authorizedGrantTypes:password,authorization_code,refresh_token}")
    private String[] authorizedGrantTypes;


    /**
     * 配置认证服务器
     *
     * @param clients 客户端
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // Basic认证客户端ID
                .withClient(clientId)
                // Basic认证密码
                .secret(passwordEncoder.encode(clientSecret))
                // 认证方式
                .authorizedGrantTypes(authorizedGrantTypes)
                // scope配置，必须配置
                .scopes(Config.SCOPES)
                // 资源ID配置，必须与资源服务器中配置的一致
                .resourceIds(Config.RESOURCE_ID);
    }


    /**
     * Token 处理器
     *
     * @return DefaultTokenServices
     */
    @Bean
    public DefaultTokenServices defaultTokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(guardRedisTokenStore);
        tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
        tokenServices.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds);
        tokenServices.setSupportRefreshToken(true);
        // 不允许重复使用刷新token，每次刷新Token后会返回新的访问Token和刷新Token，必须使用该新的刷新Token才能进行token刷新
        tokenServices.setReuseRefreshToken(false);
        return tokenServices;
    }

    /**
     * 认证服务器端点相关配置
     *
     * @param endpoints 端点
     */
    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .pathMapping("/oauth/token", "/api/oauth/token")
                // 设置Token转换器
                .accessTokenConverter(accessTokenConverter())
                // 设置用户服务类
                .userDetailsService(userService)
                // 设置认证管理器
                .authenticationManager(authenticationManager)
                // 设置Token存储处理方式
                .tokenServices(defaultTokenServices())
                // 设置异常转换器
                .exceptionTranslator(oauth2ExceptionTranslator);
    }

    /**
     * 安全配置
     *
     * @param security 安全配置对象
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 允许Web表单认证
        security.allowFormAuthenticationForClients();
    }

    /**
     * Token转换器
     *
     * @return JwtAccessTokenConverter
     */
    @Bean
    JwtAccessTokenConverter accessTokenConverter() {
        return new JwtAccessTokenConverter();
    }


}
